xlv.obj.table={
	extend:function(){
	},
	implement:function(){
		this.implement('xlv.imp.edc');
		this.implement('xlv.imp.mvc');
		this.implement('xlv.imp.dnd');
	},
	create:function(tag){
		var div=document.createElement((tag||'div'));
		div.setAttribute('xlvobject','xlv.obj.table');
		Element.addClassName(div,'class_table');
		return div;					 
	},
	properties:{
		dmName:null,
		columns:null, //[{title:'',dmPath:'',viewer:''}]
		editable:false,
		selectable:true,
		defaultStyle:true,
		tableLayout:'auto',
		showHeader:true,
		wheelEnable:false,
		refreshRow:'data', //data, rebuild
		rowSize:10
	},
	method:{
		construct:function(){
			Element.addClassName(this,'class_table');
			if(this.$P.defaultStyle)Element.addClassName(this,'default_style');
			var v=this.$V;
			v.table=xlv.util.findDirectChild(this, {tag:'table'});
			if(!v.table){
				v.table=document.createElement('table');
				v.table.setAttribute('id','xlvid_table_table');
				v.table.style.tableLayout=this.$P.tableLayout;
				v.table.setAttribute('cellpadding','0px');
				v.table.setAttribute('cellspacing','0px');
				v.table.appendChild(v.thead=document.createElement('thead'));
				v.thead.setAttribute('id','xlvid_table_head');
				v.table.appendChild(v.tbody=document.createElement('tbody'));
				v.tbody.setAttribute('id','xlvid_table_body');
				this.appendChild(v.table);
				this.styleForId(v.table,v.table.id);
				this.styleForId(v.thead,v.thead.id);
				this.styleForId(v.tbody,v.tbody.id);
				return;
			}
			v.thead=xlv.util.findDirectChild(v.table, {tag:'thead'});
			v.tbody=xlv.util.findDirectChild(v.table, {tag:'tbody'});
			if(this.$P.dmName!=null && v.tbody){
				v._pattern=[];
				while(v.tbody.rows.length>0){
					v._pattern.push(v.tbody.rows[0]);
					v.tbody.rows[0].parentNode.removeChild(v.tbody.rows[0]);
				}
				for(var r=0;r<v._pattern.length;++r){
					xlv.sys._clearExtendedFlag(v._pattern[r]);
				}
				if(v._pattern.length==0)v._pattern=null;
			}
			if(!v.tbody){
				v.tbody=document.createElement('tbody');
				v.table.appendChild(v.tbody);
			}
			if(!v.thead){
				v.thead=document.createElement('thead');
				v.table.insertBefore(v.thead,v.tbody);
			}
			v.table.setAttribute('id','xlvid_table_table');
			v.thead.setAttribute('id','xlvid_table_head');
			v.tbody.setAttribute('id','xlvid_table_body');
			this.styleForId(v.table,v.table.id);
			this.styleForId(v.thead,v.thead.id);
			this.styleForId(v.tbody,v.tbody.id);
		},
		initial:function(){
			this.dnd_properties.dragOnly=true;
			this.dnd_properties.direction='horizontal';
			if(this.$P.wheelEnable){
				var name=(Prototype.Browser.IE)?'mousewheel':'DOMMouseScroll';
				this.addEventHandler(name, '_mousewheel', this.$V.tbody);
			}
			this.addEventHandler('mousemove', '_head_mousemove',this.$V.thead);
			this.$V.rowSize=this.$P.rowSize;
			this.$V.rowStart=1;
			this.$V.selected={row:0,cell:0};
			this.editable(this.$P.editable);
			this.showHeader(this.$P.showHeader);
			this.selectable(this.$P.selectable);
			this.$V.refreshRow = this.$P.refreshRow;
			if(this.$V._pattern) this.$V.refreshRow ='rebuild';
		},
		selectable:function(enable){
			this.select_enabled(enable);
		},
		showHeader:function(show){
			this.$V.thead.style.display=(show)?'':'none';
			this.dnd_enabled(show);
		},
		editable:function(enable){
			this.$P.editable=enable;
			this.edc_enabled(enable);
		},
		setRowStart:function(row_start){
			var dm=this.mvc_getModel();
			if(dm&& row_start>0 && row_start<dm.rows()){
				this.$V.rowStart=row_start;
				this._refreshRows(dm);
			}
		},
		dispose:function(){
		},
		styleForId:function(facilities,id){
		},
		_mousewheel:function(event){
			Event.stop(event);
			var v=this.$V;
			var delta=(typeof event.wheelDelta === "undefined")?event.detail:event.wheelDelta;
			var rowstart=this.$V.rowStart;
			if(event.wheelDelta>=0||event.detail<0)	rowstart -=1;
			else rowstart +=1;
			if(rowstart<=0)return;
			if(this.$V._mousewheel)clearTimeout(this.$V._mousewheel);
			this.$V._mousewheel=setTimeout((function(){this.setRowStart(rowstart);}).bind(this), 10);
		},
		//selected
		select_enabled:function(enable){
			if(enable)this.addEventHandler('mousedown','_select_mousedown',this.$V.table);
			else this.removeObserve('mousedown','_select_mousedown',this.$V.table);
		},
		_select_mousedown:function(event){
			var td=this.targetElement(event.target,{tag:'td'});
			if(!td||!xlv.util.isDescendantOf(td, this.$V.tbody)) return;
			var tr=this.targetElement(td,{tag:'TR'});
			if(Element.hasClassName(tr,'padding'))return;
			var row_no=tr.rowIndex-this.$V.tbody.rows[0].rowIndex+this.$V.rowStart;
			var cell_no=td.cellIndex+1;
			this.setSelected(row_no,cell_no);
		},
		setSelected:function(row_no,cell_no){
			var dm=this.mvc_getModel();
			if(dm){
				if(row_no>0 && row_no!= dm.rowNumber()) {
					this.$V.cursorMove_cell_no=cell_no;
					dm.position(row_no);
					this.$V.cursorMove_cell_no=null;
				}else{
					var memo=xlv.sys.createMemoObject({src:this,from:{row:this.$V.selected.row,cell:this.$V.selected.cell}});
					memo.to={row:this.$V.selected.row,cell:this.$V.selected.cell};
					this.selected(memo);
				}
			}else this._selectRowCell(row_no, cell_no);
		},
		_selectRowCell:function(row_no,cell_no){
			if(!row_no) return;
			var memo=xlv.sys.createMemoObject({src:this,from:{row:this.$V.selected.row,cell:this.$V.selected.cell}});
			var selected=this.$V.selected;
			var prev_tr=this.getRow(selected.row);
			if(prev_tr){
				Element.removeClassName(prev_tr,'selected');
				var last_cell=this.getCell(selected.row,selected.cell);
				if(last_cell)Element.removeClassName(last_cell,'selected');
			}
			var row=this.getRow(row_no);
			cell_no=(cell_no||selected.cell);
			if(row && this.$P.selectable){
				Element.addClassName(row,'selected');
				var cell=this.getCell(row_no,cell_no);
				if(cell)Element.addClassName(cell,'selected');
			}
			selected.row=(row_no<0)? 0 :row_no;
			selected.cell=(cell_no<0)? 0 :cell_no;
			//
			memo.to={row:this.$V.selected.row,cell:this.$V.selected.cell};
			this.selected(memo);
			if(memo.from.row!=memo.to.row||memo.from.cell!=memo.to.cell){
				setTimeout((function(){Event.fire(this,'xlv:select_changed',memo);}).bind(this),1);
			}
		},
		getSelected:function(){
			return this.$V.selected;
		},
		selected:function(memo){
		},
		//count from 1
		isRowVisible:function(row_no){
			var v=this.$V;
			return (row_no>=v.rowStart) &&(row_no<v.rowStart+v.tbody.rows.length);
		},
		getRow:function(row_no){
			return this.isRowVisible(row_no) ? this.$V.tbody.rows[row_no-this.$V.rowStart]:null;
		},//count from 1
		getCell:function(row_no,cell_no){
			var r=this.getRow(row_no);
			return (r && cell_no>0&&cell_no<=r.cells.length)?r.cells[cell_no-1]:null;
		},
		_makeColumns:function(dm){
			if(!this.$P.dmName||this.$P.columns)return;
			dm=(dm||this.mvc_getModel());
			this.$P.columns=[];
			if(dm.$P.metaXPath){
				var meta=dm.xml_selectNodes(dm.$P.metaXPath+'/columns/child::*');
				for(var m=0;m<meta.length;++m){
					this.$P.columns[m]={title:meta[m].nodeName,dmPath:meta[m].nodeName,
							editor:(this.$P.editable)?{objectName:'input'}:null
							};
				}
			}else{
				var meta_r=dm.xml_selectNodes(dm.$P.dataXPath+'/r[1]/child::*');
				for(var r=0;r<meta_r.length;++r){
					this.$P.columns[r]={title:meta_r[r].nodeName,dmPath:meta_r[r].nodeName};
				}
			}
		},
		_updateHeader:function(){
			var thead=this.$V.thead;
			if(thead.rows.length>0)return;
			var row=thead.insertRow(-1);
			var columns=this.$P.columns;
			for(var c=0;c<columns.length;++c){
				var cell=row.insertCell(-1);
				if(c==0&&columns[0].dmPath=='_rowNumber_') Element.addClassName(cell,'rowNumber');
				cell.setAttribute('unselectable','on');
				this.valueForHeader(c+1,cell);
				this.styleForHeader(c+1, cell);
			}
		},
		valueForHeader:function(cell_no,cell){
			var value=this.$P.columns[cell_no-1].title;
			xlv.util.setElementValue(cell,value);
		},
		styleForHeader:function(cell_no,cell){
			var column=this.$P.columns[cell_no-1];
			if(column.width&&column.width>0)cell.style.width=column.width+'px';
			else if(this.$P.cellWidth>0)cell.style.width=this.$P.cellWidth+'px';
			if(this.$P.headHeight>0)cell.style.height=this.$P.headHeight+'px';
		},
		_refreshRows:function(dm){
			dm=(dm||this.mvc_getModel());
			var v=this.$V;
			if(v.refreshRow=='rebuild')this._disposeAllRows();
			var dm_rows=dm.rows();
			var tbody=this.$V.tbody;
			var i=0;
			for(i=0;i<v.rowSize;++i){
				if(i+v.rowStart>dm_rows) break;
				var row=tbody.rows[i];
				if(!row){
					if(v._pattern) {
						tbody.appendChild(row=v._pattern[(i+v.rowStart-1)%v._pattern.length].cloneNode(true));
						if(Prototype.Browser.IE)xlv.sys._patchIEProperties(row);
					}else row=tbody.insertRow(-1);
				}
				this.styleForRow(i+v.rowStart,row);
				var rec_row=dm.getRow(v.rowStart+i);
				Element.removeClassName(row,'inserted');
				Element.removeClassName(row,'removed');
				Element.removeClassName(row,'updated');
				Element.removeClassName(row,'even');
				Element.removeClassName(row,'odd');
				Element.removeClassName(row,'selected');
				if(dm.isInsertedRow(rec_row))Element.addClassName(row,'inserted');
				if(dm.isRemovedRow(rec_row))Element.addClassName(row,'removed');
				if(dm.isUpdatedRow(rec_row))Element.addClassName(row,'updated');
				Element.addClassName(row,(i%2)==0 ? 'even':'odd');
				if(v.rowStart+i==v.selected.row){
					Element.addClassName(row,'selected');
					var sel=v.selected.cell;
					if(sel>0&&sel<=row.cells.length)Element.addClassName(row.cells[sel-1],'selected');
				}
				for(var c=0;c<this.$P.columns.length;++c){
					var cell=(row.cells[c])? row.cells[c]:row.insertCell(-1);
					if(c==0&&this.$P.columns[c].dmPath=='_rowNumber_') Element.addClassName(cell,'rowNumber');
					this.viewerForCell(dm,v.rowStart+i,c+1,cell);
					var value=(c==0&&this.$P.columns[0].dmPath=='_rowNumber_')?''+(v.rowStart+i):this.mvc_valueFromModel(dm,v.rowStart+i,c+1);
					this.mvc_valueToView(value,v.rowStart+i,c+1);
					this.styleForCell(v.rowStart+i,c+1,cell);
				}
				while(row.cells.length>this.$P.columns.length){
					var cell=row.cells[row.cells.length-1];
					if(cell.viewer){
						xlv.sys.dispose(cell.viewer,true);
						cell.viewer=null;
					}
					cell.parentNode.removeChild(cell);
				}
			}
			while(tbody.rows.length>i){
				var cells=tbody.rows[tbody.rows.length-1].cells;
				for(var c=0;c<cells.length;++c){
					if(cells[c].viewer){
						xlv.sys.dispose(cells[c].viewer,true);
						cells[c].viewer=null;
					}
				}
				tbody.deleteRow(-1);
			}
		},
		_disposeAllRows:function(){
			var tbody=this.$V.tbody;
			while(tbody.rows.length>0){
				var cells=tbody.rows[tbody.rows.length-1].cells;
				for(var c=0;c<cells.length;++c){
					if(cells[c].viewer){
						xlv.sys.dispose(cells[c].viewer,true);
						cells[c].viewer=null;
					}
				}
				tbody.deleteRow(-1);
			}
		},
		styleForRow:function(row_no,row){
		},
		viewerForCell:function(dm,row_no,cell_no,cell){
			if(!cell.viewer){
				cell.viewer=xlv.util.findDirectChild(cell,{xlvobject:'*'});
				if(cell.viewer) xlv.sys.shell(cell.viewer);
				else{	
					var viewer=this.$P.columns[cell_no-1].viewer;
					if(viewer)cell.viewer=xlv.sys.newInstance(cell,viewer.objectName,viewer.properties,viewer.method);
				}
			}
		},
		styleForCell:function(row_no,cell_no,cell){
			cell.setAttribute('unselectable','on');
		},
		/**mvc -- row sync**/
		mvc_valueFromModel:function(dm,row_no,cell_no){
			if(!this.mvc_isModel(dm)||!row_no||!cell_no) return '';
			if(cell_no>this.$P.columns.length)return '';
			return dm.getValue(this.$P.columns[cell_no-1].dmPath,row_no);
		},
		mvc_valueToModel:function(value,dm,row_no,cell_no){
			if(!this.mvc_isModel(dm)||!row_no||!cell_no) return;
			if(cell_no>this.$P.columns.length)return;
			if(Object.isString(value))dm.setValue(value,this.$P.columns[cell_no-1].dmPath,row_no);
		},
		mvc_beforeModelChanged:function(dm,notify,memo){
		},
		mvc_modelChanged:function(dm,notify,memo){
			switch(notify){
				case 'nodeChanged':
					if(memo.type=='update'){
						var row_no=dm.rowNumber(memo.src.parentNode);
						if(this.isRowVisible(row_no)){
							for(var c=0;c<this.$P.columns.length;++c){
								var value=(c==0&&this.$P.columns[0].dmPath=='_rowNumber_')?''+row_no:this.mvc_valueFromModel(dm,row_no,c+1);
								this.mvc_valueToView(value,row_no,c+1);
							}
							row=this.getRow(row_no);
							Element.addClassName(row,'updated');
						}
					}
					break;
			}
		},
		mvc_afterModelChanged:function(dm,notify,memo){
			if(!this.mvc_isModel(dm))return;
			switch(notify){
				case 'setChanged' :
					if(memo.type=='create'){
						this.$V.rowStart=1;
						this.$V.selected={row:0,cell:0};
					}
					this._makeColumns(dm);
					this._updateHeader();
					this._refreshRows(dm);
					break;
				case 'rowChanged':
					if(memo.type== 'updated'){
						var row_no=dm.rowNumber(memo.src);
						if(this.isRowVisible(row_no)){
							row=this.getRow(row_no);
							Element.removeClassName(row,'updated');
						}
					}
					if(memo.type=='remove'||memo.type=='removed')this._refreshRows(dm);
					if(memo.type== 'insert'||memo.type=='inserted')this._refreshRows(dm);
					if(memo.type== 'cancel')this._refreshRows(dm);
					break;
				case 'cursorMove' :
					var cell_no=(this.$V.cursorMove_cell_no||this.$V.selected.cell);
					var row_no_to=dm.rowNumber(memo.to);
					if(!this.isRowVisible(row_no_to)){
						var start=row_no_to-(row_no_to%this.$V.rowSize)+1;
						start=(start>row_no_to)?start-this.$V.rowSize:start;
						this.$V.rowStart=(start>0)?start:1;
						this._refreshRows(dm);
					}
					this._selectRowCell(row_no_to,cell_no);
					break;
			}
			this.mvc_enableControl(dm);
		},
		mvc_valueToView:function(value,row_no,cell_no){
			var c=this.getCell(row_no,cell_no);
			if(c) {
				if(c.viewer && c.viewer['mvc_valueToView']) c.viewer.mvc_valueToView(value,row_no,cell_no);
				else xlv.util.setElementValue(c,value);
			}
		},
		mvc_valueFromView:function(row_no,cell_no){
			var c=this.getCell(row_no,cell_no);
			return (c)? ((c.viewer)? c.viewer.mvc_valueFromView():xlv.util.getElementValue(c)):'';
		},
		/**xlv_imp_dnd support header drag*/
		_head_mousemove:function(event){
			var td=this.targetElement(event.target,{tag:'td'});
			var thead=this.$V.thead;
			if(!td ||!xlv.util.isDescendantOf(event.target,thead)) return;
			if(td.getAttribute('id')=='xlvid_head_patch')return;
			var lt=this.getDesktop().viewportOffset(thead);
			var x=event.pointerX()-lt[0];
			x=td.offsetLeft+td.offsetWidth-x;
			if(x>=0&&x<6) td.style.cursor='col-resize';
			else td.style.cursor='';
		},
		dnd_dragable:function(event){
			var td=this.targetElement(event.target,{tag:'td'});
			return (td&&td.style.cursor=='col-resize');
		},
		dnd_createGhost:function(){
			var v=this.$V;
			var p=this.dnd_properties;
			var div=document.createElement('div');
			var style={display:'none',position:'absolute',zIndex:xlv.sys.zIndexDnd,background:'gray'};
			style.height= v.table.offsetHeight+'px';//(v.viewport.offsetHeight-(this.hasXScroller()? 17 :0))+'px';
			style.width='4px';
			Element.setStyle(div,style);
			Element.setOpacity(div,0.5);
			this.appendChild(div);
			Element.clonePosition(div,p.source,{setWidth:false,setHeight:false,offsetLeft:p.source.offsetWidth-4});
			p.ghostElement=div;
			p.ghostHomeXY=[parseInt(p.ghostElement.style.left),parseInt(p.ghostElement.style.top)];
			var view_lt=this.getDesktop().viewportOffset(this);
			var lt=this.getDesktop().viewportOffset(p.source);
			p.leftBound=Math.max(view_lt[0],lt[0]+4);
			p.rightBound=view_lt[0]+v.table.offsetWidth;
		},
		dnd_drag:function(event){
			var v=this.$V;
			var p=this.dnd_properties;
			p.source.style.cursor='';
			var ok=(p._lastXY[0]>p.leftBound && p._lastXY[0]<p.rightBound); 
			p.ghostFloat=!ok;
			return (ok) ? '' :'cancel';
		},
		dnd_dragEnd:function(event){
			var v=this.$V;
			var p=this.dnd_properties;
			p.source.style.cursor='';
			if(!p.dragCancel){
				var w=parseInt(p.source.style.width);
				if(!w)w=p.source.clientWidth;
				w=w+(p._lastXY[0]-p._startXY[0]);
				if(w>0)p.source.style.width=w+'px';
			}
		},
		////
		edc_editable:function(source){
			if(!this.$P.editable) return false;
			var td=this.targetElement(source,{tag:'td'});
			if(!td||!xlv.util.isDescendantOf(td, this.$V.tbody)) return false;
			var tr=this.targetElement(td,{tag:'tr'});
			if(Element.hasClassName(tr,'padding'))return false;
			if(td.cellIndex==0&&this.$P.columns[0].dmPath=='_rowNumber_') return false;
			if(this.$P.columns[td.cellIndex].editor)return td;
			return (td.viewer&&td.viewer.isImplement('xlv.imp.editor'))?td:false;
		},
		edc_policy:function(source){
			var td=this.targetElement(source,{tag:'td'});
			var editor=this.$P.columns[td.cellIndex].editor;
			if(editor)return xlv.util.getEditorPolicy(editor.objectName);
			if(td.viewer&&td.viewer.isImplement('xlv.imp.editor')) return {install:'viewer',dispose:'none',uninstall:'none'};
			return this.$super('xlv.imp.edc.edc_policy',source);
		},
		edc_valueFromSource:function(source){
			var td=this.targetElement(source,{tag:'td'});
			var tr=this.targetElement(td,{tag:'tr'});
			var row_no=tr.rowIndex-this.$V.tbody.rows[0].rowIndex+this.$V.rowStart;
			return this.mvc_valueFromModel(this.mvc_getModel(),row_no,td.cellIndex+1);
		},
		edc_valueToSource:function(value,source){
			var td=this.targetElement((source||this.edc_properties.source),{tag:'td'});
			var tr=this.targetElement(td,{tag:'tr'});
			var row_no=tr.rowIndex-this.$V.tbody.rows[0].rowIndex+this.$V.rowStart;
			this.mvc_valueToModel(value,this.mvc_getModel(),row_no,td.cellIndex+1);
		},
		edc_installEditor:function(value,policy){
			var p=this.edc_properties;
			var td=this.targetElement((p.source||this.edc_properties.source),{tag:'td'});
			if(policy.install=='viewer'){
				p.editor=td.viewer;
			}else{
				var editor=this.$P.columns[td.cellIndex].editor;
				var parent=(policy.install=='replace')?p.source:this.getDesktop();
				p.editor=xlv.sys.newInstance(parent,editor.objectName,editor.properties,editor.method);
				this.$super('xlv.imp.edc.edc_installEditor',value,policy);
			}
			return p.editor;
		}
	}
};
